package com.zxj.geekstudy.nio.netty;

import com.zxj.geekstudy.nio.CustomHttpClient;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.util.ReferenceCountUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * 1.（必做）整合你上次作业的 httpclient/okhttp；
 * 3.（必做）实现过滤器。
 * 4.（选做）实现路由。
 */
public class HttpHandler extends ChannelInboundHandlerAdapter {

    /**
     * 请求过滤器
     */
    HttpRequestFilter requestFilter = new HttpRequestFilter();
    /**
     * 返回过滤器
     */
    HttpResponseFilter responseFilter = new HttpResponseFilter();
    /**
     * 服务请求
     */
    CustomHttpClient customHttpClient = new CustomHttpClient();

    /**
     * 服务地址
     */
    List<String> serverUrls;

    /**
     * 使用线程池请求服务
     */
    // cpu核心数
    int cors = Runtime.getRuntime().availableProcessors();
    // 自定义线程池如何生成线程
    NamedThreadFactory threadFactory = new NamedThreadFactory("thread-zxj-");
    // keepAliveTime代表最大线程空闲多长时间退出
    // hander拒绝策略
    // 1.ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
    // 2.ThreadPoolExecutor.DiscardPolicy：丢弃任务，但是不抛出异常。如果线程队列已满，则后续提交的任务都会被丢弃，且是静默丢弃。
    // 3.ThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新提交被拒绝的任务。
    // 4.ThreadPoolExecutor.CallerRunsPolicy：由调用线程处理该任务
    private ExecutorService proxyService = new ThreadPoolExecutor(cors, cors * 2,
            1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1000),
            threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());

    public HttpHandler(List<String> serverUrls) {
        this.serverUrls = serverUrls;
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            //logger.info("channelRead流量接口请求开始，时间为{}", startTime);
            FullHttpRequest fullRequest = (FullHttpRequest) msg;
            //logger.info("接收到的请求url为{}", uri);
            proxyService.submit(() -> handler(fullRequest, ctx));
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    private void handler(FullHttpRequest fullRequest, ChannelHandlerContext ctx) {
        FullHttpResponse response = null;
        try {
            // 随机选择一个服务
            String url = ServerRouter.router(serverUrls);
            // 添加请求头
            requestFilter.filter(fullRequest);
            // 请求服务
            byte[] value = customHttpClient.get(fullRequest, url);
            response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(value));
            response.headers().set("Content-Type", "application/json");
            response.headers().setInt("Content-Length", response.content().readableBytes());
            // 添加响应头
            responseFilter.filter(response);

        } catch (Exception e) {
            System.out.println("处理出错:"+e.getMessage());
            response = new DefaultFullHttpResponse(HTTP_1_1, NO_CONTENT);
        } finally {
            if (fullRequest != null) {
                if (!HttpUtil.isKeepAlive(fullRequest)) {
                    ctx.write(response).addListener(ChannelFutureListener.CLOSE);
                } else {
                    response.headers().set(CONNECTION, KEEP_ALIVE);
                    ctx.write(response);
                }
                ctx.flush();
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

}
